﻿using System;
using System.IO;
using System.Reflection;
using System.Windows.Forms;
using System.Xml;
using McTools.Xrm.Connection;
using Microsoft.Crm.Sdk.Messages;
using Microsoft.Xrm.Sdk;
using Microsoft.Xrm.Sdk.Metadata;
using RibbonEditor.Forms.Solutions;


namespace RibbonEditor.Managers
{
    class SolutionManager 
    {
        #region Variables

        /// <summary>
        /// Organization service
        /// </summary>
        IOrganizationService service;

        /// <summary>
        /// Current retrieved solution
        /// </summary>
        Entity currentSolution;

       /// <summary>
        /// File containing ribbon definition
        /// </summary>
        XmlDocument currentCustomizationFile;

        XmlDocument solutionFile;

        string customizationFilePath;
        string solutionFilePath;

       #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of class SolutionManager
        /// </summary>
        /// <param name="organizationService">Organization service</param>
        public SolutionManager(IOrganizationService organizationService)
        {
            this.service = organizationService;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets or sets the file containing ribbon definition
        /// </summary>
        public XmlDocument CustomizationFile
        {
            get { return currentCustomizationFile; }
            set { currentCustomizationFile = value; }
        }

        public string CustomizationFilePath
        {
            get { return customizationFilePath; }
            set { customizationFilePath = value; }
        }

        public Entity CurrentSolution
        {
            get { return currentSolution; }
            set { currentSolution = value; }
        }

        

        #endregion

        public bool OpenSolution(Form mainForm, Entity solution, MetadataManager mManager, CrmConnectionStatusBar statusBar)
        {
            if (solution == null)
            {
                MethodInvoker process = delegate
                {
                    SolutionPicker picker = new SolutionPicker(this.service, mManager);
                    picker.StartPosition = FormStartPosition.CenterParent;

                    if (picker.ShowDialog(mainForm) == DialogResult.OK)
                    {
                        solution = picker.SelectedSolution;
                    }
                };

                if (mainForm.InvokeRequired)
                {
                    mainForm.Invoke(process);
                }
                else
                {
                    process();
                }
            }

            if (solution != null)
            {
                Tanguy.WinForm.Utilities.DelegatesHelpers.CommonDelegates.SetCursor(mainForm, Cursors.WaitCursor);
                statusBar.SetMessage(string.Format("Exporting solution '{0}'...", solution["uniquename"].ToString()));

                ExportSolutionRequest request = new ExportSolutionRequest()
                    {
                        Managed = false,
                        SolutionName = solution["uniquename"].ToString()
                    };

                ExportSolutionResponse response = (ExportSolutionResponse)this.service.Execute(request);

                statusBar.SetMessage("Extracting solution content...");

                byte[] compressedFile = response.ExportSolutionFile;
                string solutionName = string.Format("{0}_{1}",
                    solution["uniquename"].ToString(),
                    solution["version"].ToString().Replace(".", "_"));

                ZipManager.ExtractFilesContentFromZip(compressedFile, solutionName);

                // Stores the Xml customization file
                DirectoryInfo currentDirectory = new FileInfo(Assembly.GetExecutingAssembly().FullName).Directory;
                this.customizationFilePath = currentDirectory.FullName + "\\" + solutionName + "\\customizations.xml";
                this.currentCustomizationFile = new XmlDocument();
                this.currentCustomizationFile.Load(this.customizationFilePath);

                this.solutionFilePath = currentDirectory.FullName + "\\" + solutionName + "\\solution.xml";
                this.solutionFile = new XmlDocument();
                this.solutionFile.Load(this.solutionFilePath);

                statusBar.SetMessage("Cleaning customization file...");

                // Removes Xml parts that are not related to ribbons
                XmlNodeList list = this.currentCustomizationFile.SelectNodes("//EntityInfo");
                for (int i = 0; i < list.Count; i++)
                    list[i].ParentNode.RemoveChild(list[i]);

                list = this.currentCustomizationFile.SelectNodes("//FormXml");
                for (int i = 0; i < list.Count; i++)
                    list[i].ParentNode.RemoveChild(list[i]);

                // Save the result to the file
                this.CustomizationFile.Save(this.customizationFilePath);

                statusBar.SetMessage("Done!");

                this.currentSolution = solution;

                Tanguy.WinForm.Utilities.DelegatesHelpers.CommonDelegates.SetCursor(mainForm, Cursors.Default);
                return true;
            }
            else
            {
                Tanguy.WinForm.Utilities.DelegatesHelpers.CommonDelegates.SetCursor(mainForm, Cursors.Default);
                return false;
            }
        }

        internal void SaveRibbon()
        {
            try
            {
                this.currentCustomizationFile.Save(this.customizationFilePath);
                this.solutionFile.Save(this.solutionFilePath);
            }
            catch (Exception error)
            {
                throw new Exception("Error while saving files: " + error.Message);
            }
        }
        
        public void UpdateOrganizationService(IOrganizationService organizationService)
        {
            this.service = organizationService;
        }

        internal void ImportSolution(CrmConnectionStatusBar statusBar, bool publish)
        {
            try
            {
                statusBar.SetMessage("Compressing files...");

                // Identifiy current folder
                DirectoryInfo di = new FileInfo(this.customizationFilePath).Directory;

                // Buils folder directory to zip and output zipFile
                string folderToZip = di.FullName;
                string zipFile = di.Name + ".zip";

                // Zip folder content
                ZipManager.ZipFiles(folderToZip, zipFile);

                statusBar.SetMessage("Importing solution...");
                
                // Prepare import solution request
                ImportSolutionRequest request = new ImportSolutionRequest()
                {
                    CustomizationFile = File.ReadAllBytes(zipFile)
                };

                this.service.Execute(request);
            }
            catch (Exception error)
            {
                statusBar.SetMessage("Error!");
                throw new Exception("Error while importing solution: " + error.Message);
            }

            try
            {
                statusBar.SetMessage("Publishing solution...");

                PublishAllXmlRequest pRequest = new PublishAllXmlRequest();
                this.service.Execute(pRequest);
            }
            catch (Exception error)
            {
                statusBar.SetMessage("Error!");
                throw new Exception("Error while publishing solution: " + error.Message);
            }
           
            statusBar.SetMessage("Done!");
        }

        public void ReplaceCustomizationFilePart(string sXPathParentNodeToReplace, XmlNode newNode)
        {
            XmlNode parentNode = this.CustomizationFile.SelectSingleNode(sXPathParentNodeToReplace);

            if (parentNode.SelectSingleNode("RibbonDiffXml") != null)
            {
                parentNode.RemoveChild(parentNode.SelectSingleNode("RibbonDiffXml"));
            }

            XmlNode importedNode = this.CustomizationFile.ImportNode(newNode, true);

            if (parentNode.SelectSingleNode("Templates") != null)
            {
                parentNode.InsertAfter(importedNode, parentNode.SelectSingleNode("Templates"));
            }
            else
            {
                parentNode.AppendChild(importedNode);
            }
        }

        public void DeleteCustomizationPart(string sXPathToDelete)
        {
            XmlNode nodeToRemove = MainForm.sManager.CustomizationFile.SelectSingleNode(sXPathToDelete);

            if (nodeToRemove != null)
            {
                nodeToRemove.ParentNode.RemoveChild(nodeToRemove);
            }
        }

        public void AddComponent(int type, Guid id, string name, object data)
        {
            try
            {
                // Adding Component to server solution
                AddSolutionComponentRequest request = new AddSolutionComponentRequest()
                {
                    ComponentType = type,
                    SolutionUniqueName = currentSolution["uniquename"].ToString(),
                    ComponentId = id
                };

                this.service.Execute(request);

                // Adding information to customization file
                switch (type)
                {
                    case 1:
                        {
                            EntityMetadata emd = (EntityMetadata)data;

                            // Add Entity
                            // Adds new node to customization file
                            string sNode = string.Format(Variables.ENTITY_RIBBON_BASE,
                                emd.DisplayName.UserLocalizedLabel.Label,
                                emd.SchemaName,
                                emd.SchemaName,
                                emd.ObjectTypeCode.Value);

                            XmlTextReader xmlReader = new XmlTextReader(new StringReader(sNode));
                            XmlDocument xmlDocument = new XmlDocument();
                            XmlNode node = xmlDocument.ReadNode(xmlReader);

                            XmlNode importedNode = this.currentCustomizationFile.ImportNode(node, true);
                            this.currentCustomizationFile.SelectSingleNode("ImportExportXml/Entities").AppendChild(importedNode);
                        }
                        break;
                    case 50:
                        {
                            // Application Ribbon
                            XmlTextReader xmlReader = new XmlTextReader(new StringReader(Variables.APPLICATION_RIBBON_BASE));
                            XmlDocument xmlDocument = new XmlDocument();
                            XmlNode node = xmlDocument.ReadNode(xmlReader);

                            XmlNode importedNode = this.currentCustomizationFile.ImportNode(node, true);
                            this.currentCustomizationFile.DocumentElement.AppendChild(importedNode);
                        }
                        break;
                    case 61:
                        {
                            Entity wr = (Entity)data;

                            XmlNode webResourcesNode = this.currentCustomizationFile.SelectSingleNode("//WebResources");

                            if (webResourcesNode == null)
                            {
                                webResourcesNode = XmlHelper.AddNode(this.currentCustomizationFile.DocumentElement, "WebResources");
                            }

                            // Web Resource
                            XmlNode webResourceNode = XmlHelper.AddNode(webResourcesNode, "WebResource");

                            XmlNode IdNode = XmlHelper.AddNode(webResourceNode, "WebResourceId");
                            IdNode.InnerText = ((Guid)wr["webresourceid"]).ToString("B");

                            XmlNode nameNode = XmlHelper.AddNode(webResourceNode, "Name");
                            nameNode.InnerText = wr["name"].ToString();

                            XmlNode displayNameNode = XmlHelper.AddNode(webResourceNode, "DisplayName");
                            displayNameNode.InnerText = wr["displayname"].ToString();

                            XmlNode typeNode = XmlHelper.AddNode(webResourceNode, "WebResourceType");
                            typeNode.InnerText = ((OptionSetValue)wr["webresourcetype"]).Value.ToString() ;

                            XmlNode fileNameNode = XmlHelper.AddNode(webResourceNode, "FileName");
                            fileNameNode.InnerText = wr["filename"].ToString();
                        }
                        break;
                    default:
                        {
                            throw new Exception("Unexpected type: " + type);
                        }
                        break;
                }

                // Adding information to solution file
                XmlNode rootComponentNode = this.solutionFile.CreateElement("RootComponent");
                XmlHelper.AddAttribute(rootComponentNode, "type", type.ToString());
                XmlHelper.AddAttribute(rootComponentNode, "schemaName", name);

                this.solutionFile.SelectSingleNode("//RootComponents").AppendChild(rootComponentNode);
            }
            catch (Exception error)
            {
                throw new Exception("Error while adding solution component: " + error.Message);
            }
        }

        public void RemoveComponent(int type, Guid id, string name)
        {
            try
            {
                #region Remove component from the server solution

                RemoveSolutionComponentRequest request = new RemoveSolutionComponentRequest()
                    {
                        ComponentId = id,
                        ComponentType = type,
                        SolutionUniqueName = this.currentSolution["uniquename"].ToString()
                    };

                this.service.Execute(request);

                #endregion

                #region Remove solution component from customization file

                XmlNode componentNode = null;
                
                switch (type)
                {
                    case 1:
                        {
                            componentNode = this.CustomizationFile.SelectSingleNode("Entity[Name/@OriginalName='" + name + "']");
                        }
                        break;
                    case 50:
                        {
                            componentNode = this.CustomizationFile.SelectSingleNode("ImportExportXml/RibbonDiffXml");
                        }
                        break;
                    case 61:
                        {
                            componentNode = this.CustomizationFile.SelectSingleNode("//WebResource[Name='" + name + "']");
                        }
                        break;
                }

                if (componentNode != null)
                {
                    componentNode.ParentNode.RemoveChild(componentNode);
                }

                #endregion

                #region Remove solution component from solution file

                XmlNode solComponentNode = this.solutionFile.SelectSingleNode("ImportExportXml/SolutionManifest/RootComponents/RootComponent[@type='" + type + "' and @schemaName='" + name + "']");

                if (solComponentNode != null)
                {
                    solComponentNode.ParentNode.RemoveChild(solComponentNode);
                }

                #endregion
            }
            catch (Exception error)
            {
                throw new Exception("Error while removing solution component: " + error.Message);
            }
        }
    }
}
